<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>秒杀 | SunMoon Space</title><meta name="keywords" content="Fly"><meta name="author" content="Sun Moon"><meta name="copyright" content="Sun Moon"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="5.22、5.23 1、分页请求参数 - 谷粒商城 - 易文档 (easydoc.net) (11条消息) 谷粒商城基础篇huanhuanya66的博客-CSDN博客谷粒商城软件 (16条消息) 谷粒商城高级篇神烦狗闯入了你的博客的博客-CSDN博客谷粒商城高级 后端项目搭建分布式基础分布式6大核心：分布式ID、分布式session、分布式任务调度、分布式限流、分布式分表、分布式事务。微服务、（集">
<meta property="og:type" content="article">
<meta property="og:title" content="秒杀">
<meta property="og:url" content="https://www.sunmoon.site/2021/10/29/%E9%A1%B9%E7%9B%AE/%E7%A7%92%E6%9D%80/index.html">
<meta property="og:site_name" content="SunMoon Space">
<meta property="og:description" content="5.22、5.23 1、分页请求参数 - 谷粒商城 - 易文档 (easydoc.net) (11条消息) 谷粒商城基础篇huanhuanya66的博客-CSDN博客谷粒商城软件 (16条消息) 谷粒商城高级篇神烦狗闯入了你的博客的博客-CSDN博客谷粒商城高级 后端项目搭建分布式基础分布式6大核心：分布式ID、分布式session、分布式任务调度、分布式限流、分布式分表、分布式事务。微服务、（集">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://s1.ax1x.com/2022/05/19/OHAiM6.md.jpg">
<meta property="article:published_time" content="2021-10-28T16:37:00.000Z">
<meta property="article:modified_time" content="2022-08-25T03:56:08.390Z">
<meta property="article:author" content="Sun Moon">
<meta property="article:tag" content="Fly">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://s1.ax1x.com/2022/05/19/OHAiM6.md.jpg"><link rel="shortcut icon" href="/img/avatar.jpg"><link rel="canonical" href="https://www.sunmoon.site/2021/10/29/%E9%A1%B9%E7%9B%AE/%E7%A7%92%E6%9D%80/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: undefined,
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: {"limitCount":50,"languages":{"author":"作者: Sun Moon","link":"链接: ","source":"来源: SunMoon Space","info":"著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"}},
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: '秒杀',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-08-25 11:56:08'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/css/backgound.css"><meta name="generator" content="Hexo 5.4.2"></head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/img/avatar.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">43</div></a></div><div class="data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">25</div></a></div><div class="data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">10</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> Archives</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 链接</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/link/"><i class="fa-fw fas fa-link"></i><span> Link</span></a></li><li><a class="site-page child" href="/about/"><i class="fa-fw fas fa-heart"></i><span> About</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/aplayer/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://s1.ax1x.com/2022/05/19/OHAiM6.md.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">SunMoon Space</a></span><div id="he-plugin-simple"></div><div id="menus"><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 主页</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> Archives</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 链接</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/link/"><i class="fa-fw fas fa-link"></i><span> Link</span></a></li><li><a class="site-page child" href="/about/"><i class="fa-fw fas fa-heart"></i><span> About</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/aplayer/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">秒杀</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-10-28T16:37:00.000Z" title="发表于 2021-10-29 00:37:00">2021-10-29</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-08-25T03:56:08.390Z" title="更新于 2022-08-25 11:56:08">2022-08-25</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E9%A1%B9%E7%9B%AE/">项目</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="秒杀"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><p><strong>5.22、5.23</strong></p>
<p><a target="_blank" rel="noopener" href="https://easydoc.net/s/78237135/ZUqEdvA4/hKJTcbfd">1、分页请求参数 - 谷粒商城 - 易文档 (easydoc.net)</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/apple_52138126/article/details/123310612?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-123310612-blog-118389170.pc_relevant_antiscanv3&spm=1001.2101.3001.4242.1&utm_relevant_index=3">(11条消息) 谷粒商城基础篇<em>huanhuanya66的博客-CSDN博客</em>谷粒商城软件</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_51628741/article/details/122647909">(16条消息) 谷粒商城高级篇<em>神烦狗闯入了你的博客的博客-CSDN博客</em>谷粒商城高级</a></p>
<h1 id="后端项目搭建"><a href="#后端项目搭建" class="headerlink" title="后端项目搭建"></a>后端项目搭建</h1><h2 id="分布式基础"><a href="#分布式基础" class="headerlink" title="分布式基础"></a>分布式基础</h2><p><strong>分布式6大核心：分布式ID、分布式session、分布式任务调度、分布式限流、分布式分表、分布式事务。</strong><br><strong>微服务、（集群、分布式、节点）、远程调用、负载均衡、服务注册发现注册中心、配置中心、服务熔断降级、API网关</strong></p>
<h2 id="环境的搭建"><a href="#环境的搭建" class="headerlink" title="环境的搭建"></a>环境的搭建</h2><ul>
<li><strong>VM centos01</strong></li>
<li><strong>配置docker镜像源</strong></li>
<li><strong>安装mysql镜像，mysql5.7</strong></li>
<li><strong>安装redis镜像</strong></li>
<li><strong>自己maven3.3.1，项目3.6.1</strong></li>
</ul>
<h2 id="初始化项目"><a href="#初始化项目" class="headerlink" title="初始化项目"></a>初始化项目</h2><ul>
<li><strong>导入数据库</strong></li>
<li><strong>逆向工程生成各个模块的代码</strong></li>
<li><strong>创建各个微服务</strong></li>
<li><strong>连接gitee</strong></li>
</ul>
<p><strong>项目整合mybatis-plus，公共的 放在gulimall-common</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">*      1）、导入依赖</span><br><span class="line"> *      &lt;dependency&gt;</span><br><span class="line"> *             &lt;groupId&gt;com.baomidou&lt;/groupId&gt;</span><br><span class="line"> *             &lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;</span><br><span class="line"> *             &lt;version&gt;3.2.0&lt;/version&gt;</span><br><span class="line"> *      &lt;/dependency&gt;</span><br><span class="line"> *      2）、配置</span><br><span class="line"> *          1、配置数据源；</span><br><span class="line"> *              1）、导入数据库的驱动。https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-versions.html</span><br><span class="line"> *              2）、在application.yml配置数据源相关信息</span><br><span class="line"> *          2、配置MyBatis-Plus；</span><br><span class="line"> *              1）、使用@MapperScan</span><br><span class="line"> *              2）、告诉MyBatis-Plus，sql映射文件位置</span><br></pre></td></tr></table></figure>

<h2 id="构建微服务3大组件"><a href="#构建微服务3大组件" class="headerlink" title="构建微服务3大组件"></a>构建微服务3大组件</h2><p><a target="_blank" rel="noopener" href="https://www.mscodecloud.com/">MSCode微服务平台框架 (mscodecloud.com)</a></p>
<p><strong>不用spring Cloud Netflix中的Eureka、config、Zuul本项目用springcloudalibaba中的组件，提供了分布式应用中的需要的组件。</strong><br>**springcloudalibaba官网：(<a target="_blank" rel="noopener" href="https://github.com/alibaba/spring-cloud-alibaba/blob/2.2.x/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md">https://github.com/alibaba/spring-cloud-alibaba/blob/2.2.x/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md</a>)</p>
<h2 id="springcloud、springboot、springalibaba搭配关系"><a href="#springcloud、springboot、springalibaba搭配关系" class="headerlink" title="springcloud、springboot、springalibaba搭配关系"></a>springcloud、springboot、springalibaba搭配关系</h2><p><strong>在搭建项目前，需要确定各部分的搭配版本，否则会出现不兼容，出现各种奇怪问题。</strong><br><a target="_blank" rel="noopener" href="https://blog.csdn.net/z69183787/article/details/108912699?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-108912699-blog-123764183.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~default-1-108912699-blog-123764183.pc_relevant_default&utm_relevant_index=1">(13条消息) SpringCloud、SpringCloudAlibaba、SpringBoot 推荐版本对应关系<em>OkidoGreen的博客-CSDN博客</em>springcloudalibaba对应版本</a></p>
<p><strong>alibab组件对应关系：</strong><a target="_blank" rel="noopener" href="https://blog.csdn.net/fujiakai/article/details/122924125?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-122924125-blog-108912699.pc_relevant_paycolumn_v3&spm=1001.2101.3001.4242.2&utm_relevant_index=4">(13条消息) SpringCloud Spring Cloud Alibaba各个组件对应版本<em>神秘的凯的博客-CSDN博客</em>springcloudalibaba组件版本</a><br>** 本项目：| Spring Cloud Greenwich.SR6 |alibaba 2.1.3.RELEASE | boot 2.1.13.RELEASE  **</p>
<h2 id="nacos作为注册、配置中心"><a href="#nacos作为注册、配置中心" class="headerlink" title="nacos作为注册、配置中心"></a>nacos作为注册、配置中心</h2><h4 id="1-注册服务-EnableDiscoveryClient"><a href="#1-注册服务-EnableDiscoveryClient" class="headerlink" title="1. 注册服务@EnableDiscoveryClient"></a>1. 注册服务@EnableDiscoveryClient</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-cloud-starter-alibaba-nacos-discovery&lt;/artifactId&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br></pre></td></tr></table></figure>

<h4 id="2-想要远程调用别的服务"><a href="#2-想要远程调用别的服务" class="headerlink" title="2. 想要远程调用别的服务"></a>2. 想要远程调用别的服务</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 1）、引入open-feign</span><br><span class="line"> * 2）、编写一个接口，告诉SpringCloud这个接口需要调用远程服务</span><br><span class="line"> *   1、声明接口的每一个方法都是调用哪个远程服务的那个请求</span><br><span class="line"> * 3）、开启远程调用功能 @EnableFeignClients</span><br><span class="line"> */</span><br><span class="line">​</span><br><span class="line">    &lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-cloud-starter-openfeign&lt;/artifactId&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br></pre></td></tr></table></figure>

<h4 id="3-配置中心"><a href="#3-配置中心" class="headerlink" title="3. 配置中心"></a>3. 配置中心</h4><p><strong>给类增加@RefreshScope注解</strong><br><strong>给成员属性增减@Value</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-cloud-starter-alibaba-nacos-config</span><br><span class="line">&lt;/artifactId&gt;</span><br></pre></td></tr></table></figure>

<ol>
<li><strong>配置中心一些概念：</strong><br><strong>命名空间，配置集，配置集id，配置分组。</strong><br><strong>本项目，用命名空间区分不同系统（模块），用分组区分不同环境。</strong></li>
<li><strong>配置中心使用的总结</strong></li>
</ol>
<ul>
<li><strong>项目中最后只剩下一个bootstrap.properties文件，在给文件中说明加载配置中心哪些文件即可。</strong></li>
<li><strong>微服务中任何配置信息、配置文件都可放在配置中心。</strong></li>
<li><strong>在springboot中用于获取配置文件.properties的任何注解如@Value、@ConfigerProperties等在这里都能用。默认规则，配置中心中有的优先使用配置中心的。</strong></li>
</ul>
<p><strong>5.25</strong></p>
<h2 id="API网关、springcloud-gateway"><a href="#API网关、springcloud-gateway" class="headerlink" title="API网关、springcloud-gateway"></a>API网关、springcloud-gateway</h2><p><a target="_blank" rel="noopener" href="https://spring.io/projects/spring-cloud-gateway">Spring Cloud Gateway</a></p>
<ol>
<li><strong>三大概念</strong></li>
</ol>
<ul>
<li><strong>路由route</strong></li>
<li><strong>断言Predicate</strong></li>
<li><strong>过滤器Filter，请求到达目的之前和请求返回过程中都可以修改。</strong></li>
</ul>
<p><strong>总结：请求到达网关，网关先用断言来判定请求是否符合某个路由规则，若符合就按路由规则到指定的地方，并按照一定的过滤规则进行过滤。</strong></p>
<h1 id="前端项目搭建"><a href="#前端项目搭建" class="headerlink" title="前端项目搭建"></a>前端项目搭建</h1><h2 id="ES6"><a href="#ES6" class="headerlink" title="ES6"></a>ES6</h2><p><strong>ESMAScript是浏览器脚本语言规范，js语言是这个规范的实现。</strong><br><strong>类似于jdbc是规范，mysqlConnector是实现。</strong></p>
<h2 id="Node-js"><a href="#Node-js" class="headerlink" title="Node.js"></a>Node.js</h2><p><strong>用到NPM 是随同 NodeJS 一起安装的****包管理工具</strong>，JavaScript-NPM，Java-Maven；</p>
<h2 id="Vue"><a href="#Vue" class="headerlink" title="Vue"></a>Vue</h2><p><strong>实现数据model和页面view的****双向绑定</strong>，其中一个发生变化，另一个自动变化。</p>
<p><strong>vue的使用</strong></p>
<ul>
<li><strong>创建一个vue实例，关联页面的模板，将自己的数据（data）渲染到关联的模板上，响应式的。</strong></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;div id=&quot;app&quot;&gt;</span><br><span class="line">        &lt;input type=&quot;text&quot; v-model=&quot;num&quot;&gt;</span><br><span class="line">        &lt;button v-on:click=&quot;num++&quot;&gt;点赞&lt;/button&gt;</span><br><span class="line">        &lt;button v-on:click=&quot;cancle&quot;&gt;取消&lt;/button&gt;</span><br><span class="line">        &lt;h1&gt; &#123;&#123;name&#125;&#125; ,非常帅，有&#123;&#123;num&#125;&#125;个人为他点赞&#123;&#123;hello()&#125;&#125;&lt;/h1&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">​</span><br><span class="line">    &lt;script src=&quot;./node_modules/vue/dist/vue.js&quot;&gt;&lt;/script&gt;</span><br><span class="line">​</span><br><span class="line">    &lt;script&gt;</span><br><span class="line">        //vue声明式渲染</span><br><span class="line">        let vm = new Vue(&#123;</span><br><span class="line">            el: &quot;#app&quot;,</span><br><span class="line">            data: &#123;  </span><br><span class="line">                name: &quot;张三&quot;,</span><br><span class="line">                num: 1</span><br><span class="line">            &#125;,</span><br><span class="line">            methods:&#123;  </span><br><span class="line">                cancle()&#123;</span><br><span class="line">                    this.num -- ;</span><br><span class="line">                &#125;,</span><br><span class="line">                hello()&#123;</span><br><span class="line">                    return &quot;1&quot;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &lt;/script&gt;</span><br><span class="line">&lt;/body&gt;</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>指令简化对dom的操作。</strong></li>
<li><strong>声明方法来做更复杂的操作，methods里面可以封装方法。</strong></li>
<li><strong>el: 绑定元素，data: 封装数据，  methods:封装方法</strong></li>
</ul>
<h3 id="1-Vue指令"><a href="#1-Vue指令" class="headerlink" title="1.Vue指令"></a>1.Vue指令</h3><ul>
<li><strong>插值表达式”“，获取vue实例中定义的数据或者函数，要有返回值</strong></li>
<li><strong>v-html, v-text</strong></li>
<li>v-bind <code>给html标签的属性绑定</code>，<strong>单向绑定</strong></li>
<li>v-model 表单项，自定义组件，<strong>双向绑定</strong></li>
<li>v-on，绑定事件 简写：@；<strong>事件修饰符、按键修饰符、组合按钮</strong></li>
<li><strong>v-for，遍历数组、对象</strong></li>
<li><strong>v-if、v-show，条件判断</strong></li>
<li><strong>v-else、v-else-if</strong></li>
</ul>
<h3 id="2-计算属性和侦听器"><a href="#2-计算属性和侦听器" class="headerlink" title="2.计算属性和侦听器"></a>2.计算属性和侦听器</h3><ul>
<li><strong>计算属性computed: 某些结果基于之前数据实时计算出来</strong></li>
<li><strong>侦听器watch: 监控一个值的变化。从而做出相应的反应</strong></li>
<li><strong>过滤器filters：不改变真正的</strong><code>data</code>，而只是改变渲染的结果，并返回过滤后的版本。**<br>**局部过滤器、全局过滤器</li>
</ul>
<h3 id="3-组件化"><a href="#3-组件化" class="headerlink" title="3. 组件化"></a>3. 组件化</h3><ul>
<li><strong>通过 Vue 的 component 方法来定义一个全局组件。用来****组件复用</strong></li>
<li><strong>局部组件，在vue实例中注册在components:中。</strong></li>
</ul>
<p><strong>组件3要素：template、script、style</strong></p>
<h3 id="4-生命周期钩子函数"><a href="#4-生命周期钩子函数" class="headerlink" title="4. 生命周期钩子函数"></a>4. 生命周期钩子函数</h3><p><strong>创建实例，装载模板，渲染模板等等。Vue 为生命周期中的每个状态都设置了钩子函数（监听函数）。每当 Vue 实例处于不同的生命周期时，对应的函数就会被触发调用。</strong></p>
<h3 id="5-vue-模块化开"><a href="#5-vue-模块化开" class="headerlink" title="5. vue 模块化开"></a>5. vue 模块化开</h3><h1 id="商品服务系统"><a href="#商品服务系统" class="headerlink" title="商品服务系统"></a>商品服务系统</h1><p><strong>后台管理系统接口</strong><a target="_blank" rel="noopener" href="https://easydoc.net/s/78237135/ZUqEdvA4/6JM6txHf">https://easydoc.net/s/78237135/ZUqEdvA4/6JM6txHf</a><br><strong>bug调整</strong><br><a target="_blank" rel="noopener" href="https://wenku.baidu.com/view/eb924602ccc789eb172ded630b1c59eef8c79aea.html">https://wenku.baidu.com/view/eb924602ccc789eb172ded630b1c59eef8c79aea.html</a></p>
<h2 id="商品分类"><a href="#商品分类" class="headerlink" title="商品分类"></a>商品分类</h2><h3 id="1-查出所有分类及子分类，用树形结构返回（商品三级分类的增删改查）"><a href="#1-查出所有分类及子分类，用树形结构返回（商品三级分类的增删改查）" class="headerlink" title="1. 查出所有分类及子分类，用树形结构返回（商品三级分类的增删改查）"></a>1. 查出所有分类及子分类，用<strong>树形结构</strong>返回（商品三级分类的增删改查）</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/**</span><br><span class="line">     * 查找所有分类，以树形结构返回</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public List&lt;CategoryEntity&gt; listWithTree() &#123;</span><br><span class="line">        //1.查出所有分类</span><br><span class="line">        List&lt;CategoryEntity&gt; entities = baseMapper.selectList(null);</span><br><span class="line">        //2.组装父子树形结构</span><br><span class="line">        </span><br><span class="line">        //  2.1找到所有的1级分类</span><br><span class="line">        List&lt;CategoryEntity&gt; level1Menus = entities.stream().filter((categoryEntity)-&gt;&#123;</span><br><span class="line">            return categoryEntity.getParentCid() == 0;</span><br><span class="line">        &#125;).map((menu)-&gt;&#123; //修改这个流每个元素的属性，这里是给实体增加了一个自定义的属性</span><br><span class="line">        //  2.2 找到每1级分类的子分类getChilderns方法</span><br><span class="line">            menu.setChildern(getChilderns(menu, entities));</span><br><span class="line">            return menu;</span><br><span class="line">        &#125;).sorted((menu1, menu2)-&gt;&#123;   //对分类进行排序</span><br><span class="line">            return (menu1.getSort() == null?0:menu1.getSort()) - (menu2.getSort() == null?0:menu2.getSort());</span><br><span class="line">        &#125;).collect(Collectors.toList());</span><br><span class="line">        </span><br><span class="line">        return level1Menus;</span><br><span class="line">    &#125;</span><br><span class="line">    /**</span><br><span class="line">     * 递归查找所有菜单的子菜单</span><br><span class="line">     */</span><br><span class="line">    private List&lt;CategoryEntity&gt; getChilderns(CategoryEntity root, List&lt;CategoryEntity&gt; all)&#123;</span><br><span class="line">        List&lt;CategoryEntity&gt; childen = all.stream().filter((now)-&gt;&#123;</span><br><span class="line">            return now.getParentCid() == root.getCatId();</span><br><span class="line">        &#125;).map((categoryEntity)-&gt;&#123;  //子菜单继续找子菜单</span><br><span class="line">            categoryEntity.setChildern(getChilderns(categoryEntity, all));</span><br><span class="line">            return categoryEntity;</span><br><span class="line">        &#125;).sorted((menu1, menu2)-&gt;&#123;</span><br><span class="line">            return (menu1.getSort() == null?0:menu1.getSort()) - (menu2.getSort() == null?0:menu2.getSort());</span><br><span class="line">        &#125;).collect(Collectors.toList());</span><br><span class="line">        </span><br><span class="line">        return childen;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-跨域"><a href="#2-跨域" class="headerlink" title="2. 跨域"></a>2. 跨域</h3><ul>
<li><strong>配置****网关路由、路径重写</strong>，前端所有的请求，默认路由到后台renren-fast模块。</li>
<li><strong>解决跨域引起的错误：has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.</strong></li>
</ul>
<p><strong>跨域：</strong> 浏览器对javascript施加的一种限制，浏览器默认不让js获取远程网站的数据，js获取数据就是发ajax请求，使用xmlHttpRequest对象；该对象从本网站8001向远程的88发送请求，默认是不允许的，利用同源协议（协议、域名、端口都要相等）进行限制。跨域要发送两个请求，第一次是预检请求option，第二次才是真正的请求。**<br>**解决方案：</p>
<ol>
<li><strong>将前后端都放在一个域下，用nginx做负载均衡进行代理</strong></li>
<li><strong>采用gateway网关进行过滤，在网关里统一进行跨域配置。</strong></li>
</ol>
<p><strong>5.26</strong></p>
<h3 id="3-前端三级分类组件"><a href="#3-前端三级分类组件" class="headerlink" title="3. 前端三级分类组件"></a>3. 前端三级分类组件</h3><p><a target="_blank" rel="noopener" href="https://element.eleme.cn/#/zh-CN/component/tree">组件 | Element</a></p>
<h3 id="4-三级分类组件中部分的逻辑删除"><a href="#4-三级分类组件中部分的逻辑删除" class="headerlink" title="4. 三级分类组件中部分的逻辑删除"></a>4. 三级分类组件中部分的<strong>逻辑删除</strong></h3><p><strong>mybatis-plus中的逻辑删除</strong><br><a target="_blank" rel="noopener" href="https://baomidou.com/pages/6b03c5/">逻辑删除 | MyBatis-Plus (baomidou.com)</a></p>
<ul>
<li><strong>application.yml加入配置（表中用show_status表示）</strong></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">mybatis-plus:</span><br><span class="line">  global-config:</span><br><span class="line">    db-config:</span><br><span class="line">      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)</span><br><span class="line">      logic-delete-value: 1 # 逻辑已删除值(默认为 1)</span><br><span class="line">      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>实体类某字段（show_status属性）加上</strong><code>@TableLogic</code>注解</li>
</ul>
<h2 id="品牌管理"><a href="#品牌管理" class="headerlink" title="品牌管理"></a>品牌管理</h2><p><strong>导入renren-generate生成的vue；修改细节部分。</strong><br><strong>监听“显示状态”，发生改变后，向服务器发送请求。</strong></p>
<h3 id="aliyun对象存储OSS"><a href="#aliyun对象存储OSS" class="headerlink" title="aliyun对象存储OSS"></a>aliyun对象存储OSS</h3><ul>
<li><strong>客户端上传的3种方式：</strong></li>
</ul>
<ol>
<li><strong>客户端提交给服务器，服务器在上传，多一道手续没必要。</strong></li>
<li><strong>客户端直接上传给服务器，需要服务器给客户端文件存储系统的密码，会发生密码泄露。</strong></li>
<li><strong>服务端签名后上传。客户端没有向服务端索要上传密码，索要的是服务端生成的防伪签名，之后由服务端带着这个签名上传到文件存储服务器。阿里云来验证这个签名是否合法，若合法则允许其上传。</strong></li>
</ol>
<ul>
<li><strong>通过java代码（sdk方式）上传文件到aliyun</strong><br><a target="_blank" rel="noopener" href="https://help.aliyun.com/document_detail/84781.html">https://help.aliyun.com/document_detail&#x2F;84781.html</a><br><strong>第一种方式时采用sdk的方式，配置pom文件中的sdk，还要修改代码代码。</strong></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">（gulimall-product）</span><br><span class="line">&lt;dependency&gt;</span><br><span class="line">                    &lt;groupId&gt;com.aliyun.oss&lt;/groupId&gt;</span><br><span class="line">                    &lt;artifactId&gt;aliyun-sdk-oss&lt;/artifactId&gt;</span><br><span class="line">                    &lt;version&gt;3.5.0&lt;/version&gt;</span><br><span class="line">                &lt;/dependency&gt;</span><br><span class="line">@Test</span><br><span class="line">    public static void main(String[] args) throws Exception &#123;</span><br><span class="line">        // Endpoint以华东1（杭州）为例，其它Region请按实际情况填写。</span><br><span class="line">        String endpoint = &quot;https://oss-cn-shanghai.aliyuncs.com&quot;;</span><br><span class="line">        // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。</span><br><span class="line">        String accessKeyId = &quot;LTAI5t9ok5G8JD7KzBV5Qezz&quot;;</span><br><span class="line">        String accessKeySecret = &quot;CCWodyefgFuYbDcH27OOEvOu60JlQi&quot;;</span><br><span class="line">        // 填写Bucket名称，例如examplebucket。</span><br><span class="line">        String bucketName = &quot;gulimall-heyhello&quot;;</span><br><span class="line">        // 填写Object完整路径，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。</span><br><span class="line">        String objectName = &quot;test/4.jpg&quot;;</span><br><span class="line">        // 填写本地文件的完整路径，例如D:\\localpath\\examplefile.txt。</span><br><span class="line">        // 如果未指定本地路径，则默认从示例程序所属项目对应本地路径中上传文件流。</span><br><span class="line">        String filePath= &quot;C:\\Users\\Mr Li\\Pictures\\新\\4.jpg&quot;;</span><br><span class="line">​</span><br><span class="line">        // 创建OSSClient实例。</span><br><span class="line">        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);</span><br><span class="line">​</span><br><span class="line">        try &#123;</span><br><span class="line">            InputStream inputStream = new FileInputStream(filePath);</span><br><span class="line">            // 创建PutObject请求。</span><br><span class="line">            ossClient.putObject(bucketName, objectName, inputStream);</span><br><span class="line">        &#125; catch (OSSException oe) &#123;</span><br><span class="line">            System.out.println(&quot;Caught an OSSException, which means your request made it to OSS, &quot;</span><br><span class="line">                    + &quot;but was rejected with an error response for some reason.&quot;);</span><br><span class="line">            System.out.println(&quot;Error Message:&quot; + oe.getErrorMessage());</span><br><span class="line">            System.out.println(&quot;Error Code:&quot; + oe.getErrorCode());</span><br><span class="line">            System.out.println(&quot;Request ID:&quot; + oe.getRequestId());</span><br><span class="line">            System.out.println(&quot;Host ID:&quot; + oe.getHostId());</span><br><span class="line">        &#125; catch (ClientException ce) &#123;</span><br><span class="line">            System.out.println(&quot;Caught an ClientException, which means the client encountered &quot;</span><br><span class="line">                    + &quot;a serious internal problem while trying to communicate with OSS, &quot;</span><br><span class="line">                    + &quot;such as not being able to access the network.&quot;);</span><br><span class="line">            System.out.println(&quot;Error Message:&quot; + ce.getMessage());</span><br><span class="line">        &#125; finally &#123;</span><br><span class="line">            if (ossClient != null) &#123;</span><br><span class="line">                ossClient.shutdown();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>采用封装号的对象进行存储（在gulimall-common中依赖spring-cloud-starter-alicloud-oss），进行简单配置即可</strong><br><a target="_blank" rel="noopener" href="https://github.com/alibaba/aliyun-spring-boot/tree/master/aliyun-spring-boot-samples/aliyun-oss-spring-boot-sample">aliyun-spring-boot&#x2F;aliyun-spring-boot-samples&#x2F;aliyun-oss-spring-boot-sample at master · alibaba&#x2F;aliyun-spring-boot (github.com)</a></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">&lt;dependency&gt;</span><br><span class="line">            &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;</span><br><span class="line">            &lt;artifactId&gt;spring-cloud-starter-alicloud-oss&lt;/artifactId&gt;</span><br><span class="line">        &lt;/dependency&gt;</span><br><span class="line">​</span><br><span class="line">配置：</span><br><span class="line"> alicloud:</span><br><span class="line">      access-key: LTAI5t9ok5G8JD。。。</span><br><span class="line">      secret-key: CCWodyefgFuYbDcH，，</span><br><span class="line">      oss:</span><br><span class="line">        endpoint: https://oss-cn-shanghai.aliyuncs.com</span><br></pre></td></tr></table></figure>

<p><strong>加了上面依赖，配置好相关信息，****直接注入ossClient对象即可</strong>，无需创建。**<br>**<strong>总结：</strong></p>
<ol>
<li><strong>引入oss-starter</strong></li>
<li><strong>配置key、endpoint、screet等相关参数</strong></li>
<li><strong>使用OSSClient</strong></li>
</ol>
<h3 id="服务端签名上传文件"><a href="#服务端签名上传文件" class="headerlink" title="服务端签名上传文件"></a>服务端签名上传文件</h3><p><a target="_blank" rel="noopener" href="https://help.aliyun.com/document_detail/91868.html">Java (aliyun.com)</a></p>
<p><strong>@RestController &#x3D; @Controller + @RequestBody,将请求响应体以json格式返回。</strong></p>
<ol>
<li><strong>后端返回签名的controller</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">public class OssController &#123;</span><br><span class="line">​</span><br><span class="line">    @Autowired</span><br><span class="line">    OSS ossClient;</span><br><span class="line">​</span><br><span class="line">    @Value(&quot;$&#123;spring.cloud.alicloud.oss.bucket&#125;&quot;)</span><br><span class="line">    private String bucket;</span><br><span class="line">​</span><br><span class="line">    @Value(&quot;$&#123;spring.cloud.alicloud.oss.endpoint&#125;&quot;)</span><br><span class="line">    private String endpoint;</span><br><span class="line">​</span><br><span class="line">    @Value(&quot;$&#123;spring.cloud.alicloud.oss.access-key&#125;&quot;)</span><br><span class="line">    private String accessId;</span><br><span class="line">​</span><br><span class="line">    /**</span><br><span class="line">     * 返回签名</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @RequestMapping(&quot;/oss/policy&quot;)</span><br><span class="line">    public R  policy()&#123;</span><br><span class="line">​</span><br><span class="line">         // 填写Host地址，格式为https://bucketname.endpoint。</span><br><span class="line">        String host = &quot;https://&quot;+bucket+&quot;.&quot;+endpoint;</span><br><span class="line">​</span><br><span class="line">       </span><br><span class="line">        // 根据日期进行分类，按照天进行分类</span><br><span class="line">        String format = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;).format(new Date());</span><br><span class="line">        String dir = format+&quot;/&quot;;</span><br><span class="line">​</span><br><span class="line">​</span><br><span class="line">        Map&lt;String, String&gt; respMap = null;</span><br><span class="line">        try &#123;</span><br><span class="line">            long expireTime = 30;</span><br><span class="line">            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;</span><br><span class="line">            Date expiration = new Date(expireEndTime);</span><br><span class="line">            PolicyConditions policyConds = new PolicyConditions();</span><br><span class="line">            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);</span><br><span class="line">            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);</span><br><span class="line">​</span><br><span class="line">            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);</span><br><span class="line">            byte[] binaryData = postPolicy.getBytes(&quot;utf-8&quot;);</span><br><span class="line">            String encodedPolicy = BinaryUtil.toBase64String(binaryData);</span><br><span class="line">            String postSignature = ossClient.calculatePostSignature(postPolicy);</span><br><span class="line">​</span><br><span class="line">            respMap = new LinkedHashMap&lt;String, String&gt;();</span><br><span class="line">            respMap.put(&quot;accessid&quot;, accessId);</span><br><span class="line">            respMap.put(&quot;policy&quot;, encodedPolicy);</span><br><span class="line">            respMap.put(&quot;signature&quot;, postSignature);</span><br><span class="line">            respMap.put(&quot;dir&quot;, dir);</span><br><span class="line">            respMap.put(&quot;host&quot;, host);</span><br><span class="line">            respMap.put(&quot;expire&quot;, String.valueOf(expireEndTime / 1000));</span><br><span class="line">            // respMap.put(&quot;expire&quot;, formatISO8601Date(expiration));</span><br><span class="line">​</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            // Assert.fail(e.getMessage());</span><br><span class="line">            System.out.println(e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line">        return R.ok().put(&quot;data&quot;, respMap);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li><strong>前端索要签名</strong></li>
</ol>
<p><strong>将upload放在组件components中，里面放了三个文件：单文件上传vue、多文件上传vue、索要签名的函数js文件</strong><br><strong>修改vue中的action（</strong><code>https://gulimall-heyhello.oss-cn-shanghai.aliyuncs.com</code>）、js文件中的索要地址url；**<br>**在上传组件的位置（script里面import组件）导入组件</p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">import singleUpload from &quot;@/components/upload/singleUpload&quot;</span><br></pre></td></tr></table></figure>

<ol start="3">
<li><strong>要在阿里云oss中配置，允许跨域访问</strong></li>
</ol>
<h3 id="前后端表单数据都进行校验JSR303"><a href="#前后端表单数据都进行校验JSR303" class="headerlink" title="前后端表单数据都进行校验JSR303"></a>前后端表单数据都进行校验JSR303</h3><p><strong>前端：随每一个数据增加约束</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">firstLetter: [</span><br><span class="line">          &#123;</span><br><span class="line">            validator: (rule, value, callback) =&gt; &#123;</span><br><span class="line">              if (value == &quot;&quot;) &#123;</span><br><span class="line">                callback(new Error(&quot;首字母必须填写&quot;));</span><br><span class="line">              &#125; else if (!/^[a-zA-Z]$/.test(value)) &#123;</span><br><span class="line">                callback(new Error(&quot;首字母必须a-z或者A-Z之间&quot;));</span><br><span class="line">              &#125; else &#123;</span><br><span class="line">                callback();</span><br><span class="line">              &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            trigger: &quot;blur&quot;</span><br><span class="line">          &#125;</span><br><span class="line">        ],</span><br></pre></td></tr></table></figure>

<p><strong>5.27</strong><br><strong>后端：JSR303功能</strong></p>
<ol>
<li><strong>需要校验的数据增加校验注解，并定义自己的message提示，所有注解来源于javax.validation.constrations;  @Future,@NotNull</strong></li>
<li><strong>对controller方法中括号里引用的参数，增加@Valid；校验错误时候提示message消息</strong></li>
<li><strong>给校验的bean后紧跟一个BindingResult，就可以获取校验的结果</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 品牌名</span><br><span class="line"> */</span><br><span class="line">@NotBlank(message = &quot;品牌名必须提交！&quot;)</span><br><span class="line">private String name;</span><br><span class="line">​</span><br><span class="line">​</span><br><span class="line"> /**</span><br><span class="line">     * 保存</span><br><span class="line">     */</span><br><span class="line">     @RequestMapping(&quot;/save&quot;)</span><br><span class="line">    public R save(@Valid @RequestBody BrandEntity brand , BindingResult result)&#123;</span><br><span class="line">​</span><br><span class="line">        if (result.hasErrors())&#123;</span><br><span class="line">            HashMap&lt;String, String&gt; map = new HashMap&lt;&gt;();</span><br><span class="line">            result.getFieldErrors().forEach((item)-&gt;&#123;</span><br><span class="line">                String message = item.getDefaultMessage();</span><br><span class="line">                String filed = item.getField();</span><br><span class="line">                map.put(filed, message);</span><br><span class="line">            &#125;);</span><br><span class="line">            return R.error(400,&quot;提交的数据不合法！&quot;).put(&quot;data&quot;, map);</span><br><span class="line">        &#125;</span><br><span class="line">brandService.save(brand);</span><br><span class="line">​</span><br><span class="line">        return R.ok();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h3 id="统一异常处理中进行校验，controllerAdvice"><a href="#统一异常处理中进行校验，controllerAdvice" class="headerlink" title="统一异常处理中进行校验，controllerAdvice"></a>统一异常处理中进行校验，controllerAdvice</h3><ol>
<li><strong>写异常处理类，统一处理MethodArgumentNotValidException异常，controller不进行异常处理，直接抛出去</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@Slf4j</span><br><span class="line">@RestControllerAdvice(basePackages = &quot;com.atguigu.gulimall.product.controller&quot;)</span><br><span class="line">@ResponseBody</span><br><span class="line">public class GulimallExceptionControllerAdvice &#123;</span><br><span class="line">​</span><br><span class="line">    @ExceptionHandler(value = MethodArgumentNotValidException.class)</span><br><span class="line">    public R handlerValidException(MethodArgumentNotValidException e) &#123;</span><br><span class="line">        log.error(&quot;数据校验出现错误&#123;&#125;,异常类型&#123;&#125;&quot;, e.getMessage(), e.getClass());</span><br><span class="line">        BindingResult result = e.getBindingResult();</span><br><span class="line">     </span><br><span class="line">        HashMap&lt;String, String&gt; map = new HashMap&lt;&gt;();</span><br><span class="line">        result.getFieldErrors().forEach((item) -&gt; &#123;</span><br><span class="line">            String message = item.getDefaultMessage();</span><br><span class="line">            String filed = item.getField();</span><br><span class="line">            map.put(filed, message);</span><br><span class="line">        &#125;);</span><br><span class="line">        return R.error(400, &quot;提交的数据不合法！&quot;).put(&quot;data&quot;, map);</span><br><span class="line">    &#125;</span><br><span class="line">​</span><br><span class="line">    @ExceptionHandler(value = Throwable.class)//处理其他异常</span><br><span class="line">    public R handlerValidException(Throwable e) &#123;</span><br><span class="line">        return R.error();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li><strong>改进：在common模块中定义一个错误码枚举类，整个项目都可以调用</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">/***</span><br><span class="line"> * 错误码和错误信息定义类</span><br><span class="line"> * 1. 错误码定义规则为5为数字</span><br><span class="line"> * 2. 前两位表示业务场景，最后三位表示错误码。例如：100001。10:通用 001:系统未知异常</span><br><span class="line"> * 3. 维护错误码后需要维护错误描述，将他们定义为枚举形式</span><br><span class="line"> * 错误码列表：</span><br><span class="line"> *  10: 通用</span><br><span class="line"> *      001：参数格式校验</span><br><span class="line"> *  11: 商品</span><br><span class="line"> *  12: 订单</span><br><span class="line"> *  13: 购物车</span><br><span class="line"> *  14: 物流</span><br><span class="line"> *</span><br><span class="line"> *</span><br><span class="line"> */</span><br><span class="line">​</span><br><span class="line">public enum BizCodeEnume &#123;</span><br><span class="line">    UNKNOW_EXCEPTION(10000,&quot;系统未知异常&quot;),</span><br><span class="line">    VAILD_EXCEPTION(10001,&quot;参数格式校验失败&quot;);</span><br><span class="line">​</span><br><span class="line">    private int code;</span><br><span class="line">    private String msg;</span><br><span class="line">    BizCodeEnume(int code,String msg)&#123;</span><br><span class="line">        this.code = code;</span><br><span class="line">        this.msg = msg;</span><br><span class="line">    &#125;</span><br><span class="line">​</span><br><span class="line">    public int getCode() &#123;</span><br><span class="line">        return code;</span><br><span class="line">    &#125;</span><br><span class="line">​</span><br><span class="line">    public String getMsg() &#123;</span><br><span class="line">        return msg;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="分组数据校验"><a href="#分组数据校验" class="headerlink" title="分组数据校验"></a>分组数据校验</h3><p><strong>例如：品牌实体，新增时参数不能带id，因为是自增的，修改的时候参数又必须携带参数名；可应用JSR303分组功能；</strong></p>
<ol>
<li><strong>给校验注解进行分组。</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@NotNull(message = &quot;修改必须指定品牌id&quot;, groups = &#123;UpdateGroup.class&#125;)</span><br><span class="line">@Null(message = &quot;新增不指定品牌id&quot;, groups = &#123;AddGroup.class&#125;)</span><br><span class="line">@TableId</span><br><span class="line">private Long brandId;</span><br><span class="line">/**</span><br><span class="line"> * 品牌名</span><br><span class="line"> */</span><br><span class="line">@NotBlank(message = &quot;品牌名必须提交！&quot;, groups = &#123;UpdateGroup.class, AddGroup.class&#125;)</span><br><span class="line">private String name;</span><br></pre></td></tr></table></figure>

<ol start="2">
<li><strong>controller中添加 @Validated({AddGroup.class})</strong></li>
</ol>
<h3 id="自定义校验"><a href="#自定义校验" class="headerlink" title="自定义校验"></a>自定义校验</h3><p><strong>完成BrandEntity中的showStatus属性只能为0或者1</strong></p>
<ol>
<li><strong>编写一个自定义校验注解</strong></li>
<li><strong>编写一个自定义校验器</strong></li>
<li><strong>关联自定义校验器与校验注解</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">1. 自定义校验注解</span><br><span class="line">​</span><br><span class="line">@Documented</span><br><span class="line">【这里可以绑定多个不同的校验注解器】</span><br><span class="line">@Constraint(validatedBy = &#123; ListValueConstraintValidator.class &#125;)</span><br><span class="line">@Target(&#123; METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE &#125;)</span><br><span class="line">@Retention(RUNTIME)</span><br><span class="line">public @interface ListValue &#123;</span><br><span class="line">    String message() default &quot;&#123;com.atguigu.common.valid.ListValue.message&#125;&quot;; //配置里面的信息</span><br><span class="line">​</span><br><span class="line">    Class&lt;?&gt;[] groups() default &#123; &#125;;</span><br><span class="line">​</span><br><span class="line">    Class&lt;? extends Payload&gt;[] payload() default &#123; &#125;;</span><br><span class="line">​</span><br><span class="line">    int[] vals() default &#123; &#125;;</span><br><span class="line">&#125;</span><br><span class="line">​</span><br><span class="line">​</span><br><span class="line">2. 自定义校验器</span><br><span class="line">​</span><br><span class="line">public class ListValueConstraintValidator implements ConstraintValidator&lt;ListValue,Integer&gt; &#123;</span><br><span class="line">​</span><br><span class="line">    private Set&lt;Integer&gt; set = new HashSet&lt;&gt;();</span><br><span class="line">    //初始化方法</span><br><span class="line">    @Override</span><br><span class="line">    public void initialize(ListValue constraintAnnotation) &#123;</span><br><span class="line">​</span><br><span class="line">        int[] vals = constraintAnnotation.vals();</span><br><span class="line">        for (int val : vals) &#123;</span><br><span class="line">            set.add(val);</span><br><span class="line">        &#125;</span><br><span class="line">​</span><br><span class="line">    &#125;</span><br><span class="line">​</span><br><span class="line">    //判断是否校验成功</span><br><span class="line">​</span><br><span class="line">    /**</span><br><span class="line">     *</span><br><span class="line">     * @param value 需要校验的值</span><br><span class="line">     * @param context</span><br><span class="line">     * @return</span><br><span class="line">     */</span><br><span class="line">    @Override</span><br><span class="line">    public boolean isValid(Integer value, ConstraintValidatorContext context) &#123;</span><br><span class="line">        return set.contains(value);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="mybatis-plus分页插件"><a href="#mybatis-plus分页插件" class="headerlink" title="mybatis-plus分页插件"></a>mybatis-plus分页插件</h3><p><a target="_blank" rel="noopener" href="https://www.mybatis-plus.com/guide/page.html">分页插件 | MyBatis-Plus</a></p>
<h2 id="平台属性"><a href="#平台属性" class="headerlink" title="平台属性"></a>平台属性</h2><h3 id="spu、sku、规格参数、销售属性"><a href="#spu、sku、规格参数、销售属性" class="headerlink" title="spu、sku、规格参数、销售属性"></a>spu、sku、规格参数、销售属性</h3><p><strong>spu决定规格参数，sku决定销售属性。</strong></p>
<p><a target="_blank" rel="noopener" href="https://imgtu.com/i/XejYLV"><img src="https://s1.ax1x.com/2022/05/27/XejYLV.png" alt="XejYLV.png"></a></p>
<h3 id="级联更新"><a href="#级联更新" class="headerlink" title="级联更新"></a>级联更新</h3><p><strong>在电商系统中，很多查询不采用连表查询，存在很多【中间表】进行冗余存储，例如pms_category_brand_relation表，品牌和分类的关系采用中间表存储，因此例如在品牌表中的品牌名发生改变时，要保证也让pms_category_brand_relation关系标中的品牌名同步。</strong></p>
<p><strong>在业务代码中，将所有冗余存储中的数据更新。</strong></p>
<h3 id="Object划分、VO应用"><a href="#Object划分、VO应用" class="headerlink" title="Object划分、VO应用"></a>Object划分、VO应用</h3><p><strong>View ject视图对象，接受页面传来的数据，封装对象；将业务处理完的对象，封装成页面要的数据。</strong><br><strong>【规格参数】应用。</strong><br><strong>【销售属性】应用。</strong></p>
<h2 id="商品维护"><a href="#商品维护" class="headerlink" title="商品维护"></a>商品维护</h2><p><code>/product/attrgroup/225/withattr?t=1653961063823</code>开始，复制</p>
<p><code>http://localhost:88/api/product/skuinfo/list?t=1653962976913&amp;page=1&amp;limit=10&amp;key=&amp;catelogId=0&amp;brandId=0&amp;min=0&amp;max=0</code></p>
<p><strong>网上有可以将json数据转为entity格式的工具。</strong></p>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">@RequestMapping(&quot;/save&quot;)</span><br><span class="line">    //@RequiresPermissions(&quot;product:spuinfo:save&quot;)</span><br><span class="line">    public R save(@RequestBody SpuSaveVo vo)</span><br></pre></td></tr></table></figure>

<h1 id="库存系统"><a href="#库存系统" class="headerlink" title="库存系统"></a>库存系统</h1><p><strong>采购需求由自动添加和人工添加两种</strong><br><strong>合并采购单（人工合并或系统定时合并），将合并单分分配给某个采购人员，采购入库后，添加库存。</strong></p>
<p><strong>商品管理-商品spu规格维护</strong></p>
<p><strong>高级开始，20220612</strong></p>
<h1 id="ElasticSearch全文检索功能"><a href="#ElasticSearch全文检索功能" class="headerlink" title="ElasticSearch全文检索功能"></a>ElasticSearch全文检索功能</h1><p><strong>一个存储、分析、检索的引擎！</strong><br><strong>全文检索的数据类源于mysql，进行数据的复杂分析</strong><br><strong>用法和mysql类似，先安装，采用docker安装</strong></p>
<h2 id="存储"><a href="#存储" class="headerlink" title="存储"></a>存储</h2><p><strong>对es的所有操作，都被封装为RestAPI，发送rest请求即可。</strong></p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><h2 id="检索"><a href="#检索" class="headerlink" title="检索"></a>检索</h2><h2 id="分词器"><a href="#分词器" class="headerlink" title="分词器"></a>分词器</h2><p><strong>java操作es</strong><br><strong>两种方法：</strong><br><strong>1操作9300端口；</strong><br><strong>2.操作9200端口：向es发送rest请求。采用官方的Elasticsearch-Rest-Client，对各种操作进行了封装。</strong></p>
<p><strong>请求参数设置</strong><br><a target="_blank" rel="noopener" href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-low-usage-requests.html#java-rest-low-usage-request-options">https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.4/java-rest-low-usage-requests.html#java-rest-low-usage-request-options</a></p>
<p><strong>用java操作es</strong></p>
<ul>
<li><strong>保存更新</strong><br><strong>创建一个保存操作对象IndexRequest；执行操作client.index；提取有用的响应数据。</strong></li>
<li><strong>检索</strong><br><strong>创建检索对象；</strong></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">//创建检索对象</span><br><span class="line">        SearchRequest searchRequest = new SearchRequest();</span><br><span class="line">        searchRequest.indices(&quot;bank&quot;);</span><br><span class="line">​</span><br><span class="line">        //指定检索条件</span><br><span class="line">        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();</span><br><span class="line">        sourceBuilder.query(QueryBuilders.matchQuery(&quot;address&quot;,&quot;mill&quot;));</span><br><span class="line">//        sourceBuilder.from();</span><br><span class="line">//        sourceBuilder.size();</span><br><span class="line">        searchRequest.source(sourceBuilder);</span><br><span class="line">        System.out.println(sourceBuilder.toString());</span><br><span class="line">        //分析结果</span><br><span class="line">        SearchResponse searchResponse = client.search(searchRequest, GulimallElasticSearchConfig.COMMON_OPTIONS);</span><br><span class="line">        System.out.println(searchResponse.toString());</span><br></pre></td></tr></table></figure>

<h2 id="sku在es中的存储模型"><a href="#sku在es中的存储模型" class="headerlink" title="sku在es中的存储模型"></a>sku在es中的存储模型</h2><p><strong>es中的数据是保存在内存中的，不能无限制的将数据都放在es中，要分析放那些有用的数据。</strong><br><strong>sku标题、sku销量、sku价格、品牌分类、规格检索</strong></p>
<h2 id="返回值R的设计细节"><a href="#返回值R的设计细节" class="headerlink" title="返回值R的设计细节"></a>返回值R的设计细节</h2><p><strong>在远程调用库存系统，返回SkuHasStockVo时，是将其放在R中的data属性中，还需采用get方法得到data，转换类型。</strong></p>
<ol>
<li><strong>将R中增加泛型data属性，放的时候直接放入对应对象即可；</strong></li>
<li><strong>远程controller的结果不封装在R，直接返回要求的对象结果即可。</strong></li>
<li><strong>自己封装解析结果，data</strong></li>
</ol>
<h2 id="feign的调用流程"><a href="#feign的调用流程" class="headerlink" title="feign的调用流程"></a>feign的调用流程</h2><ol>
<li><strong>构造请求数据，讲对象转为json</strong></li>
<li><strong>发送请求，进行执行，并解码响应数据</strong></li>
<li><strong>执行请求会有重试机制</strong></li>
</ol>
<h2 id="商城业务thymleaf开发"><a href="#商城业务thymleaf开发" class="headerlink" title="商城业务thymleaf开发"></a>商城业务thymleaf开发</h2><ol>
<li><strong>thymleaf-starter、关闭缓存</strong></li>
<li><strong>静态资源放在static下</strong></li>
<li><strong>页面放在templates，直接访问springboot，访问项目时候，默认会找index。</strong></li>
<li><strong>页面修改不重启服务情况下实时更新</strong></li>
</ol>
<ul>
<li><strong>引入dev-tools（springboot提供的）</strong><optional><strong>true</strong></optional></li>
<li><strong>关掉thymleaf的缓存，修改后ctr+shift+f9重新编译页面</strong></li>
</ul>
<p><strong>thymleaf常用命令</strong></p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">Sun Moon</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://www.sunmoon.site/2021/10/29/%E9%A1%B9%E7%9B%AE/%E7%A7%92%E6%9D%80/">https://www.sunmoon.site/2021/10/29/%E9%A1%B9%E7%9B%AE/%E7%A7%92%E6%9D%80/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://www.sunmoon.site" target="_blank">SunMoon Space</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"></div><div class="post_share"><div class="social-share" data-image="https://s1.ax1x.com/2022/05/19/OHAiM6.md.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="/img/wechat.jpg" target="_blank"><img class="post-qr-code-img" src="/img/wechat.jpg" alt="wechat"/></a><div class="post-qr-code-desc">wechat</div></li><li class="reward-item"><a href="/img/alipay.jpg" target="_blank"><img class="post-qr-code-img" src="/img/alipay.jpg" alt="alipay"/></a><div class="post-qr-code-desc">alipay</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2021/12/29/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/Linux/"><img class="prev-cover" src="https://s1.ax1x.com/2022/05/19/OHAFsK.md.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Linux知识点</div></div></a></div><div class="next-post pull-right"><a href="/2021/05/01/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%A0%86/"><img class="next-cover" src="https://s1.ax1x.com/2022/05/19/OHApGR.md.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">堆</div></div></a></div></nav></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/img/avatar.jpg" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">Sun Moon</div><div class="author-info__description">Hi, welcome to my blog!</div></div><div class="card-info-data is-center"><div class="card-info-data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">43</div></a></div><div class="card-info-data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">25</div></a></div><div class="card-info-data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">10</div></a></div></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/sun-moon22"><i class="fab fa-github"></i><span>关注我</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/sun-moon22" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="https://qm.qq.com/cgi-bin/qm/qr?k=zftgTJPReW-aEsUnWx6dKFfD03rovP8a&amp;noverify=0" target="_blank" title="QQ"><i class="fas fa-envelope"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">欢迎来到我的博客！</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%90%8E%E7%AB%AF%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA"><span class="toc-number">1.</span> <span class="toc-text">后端项目搭建</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E5%9F%BA%E7%A1%80"><span class="toc-number">1.1.</span> <span class="toc-text">分布式基础</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%8E%AF%E5%A2%83%E7%9A%84%E6%90%AD%E5%BB%BA"><span class="toc-number">1.2.</span> <span class="toc-text">环境的搭建</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE"><span class="toc-number">1.3.</span> <span class="toc-text">初始化项目</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9E%84%E5%BB%BA%E5%BE%AE%E6%9C%8D%E5%8A%A13%E5%A4%A7%E7%BB%84%E4%BB%B6"><span class="toc-number">1.4.</span> <span class="toc-text">构建微服务3大组件</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#springcloud%E3%80%81springboot%E3%80%81springalibaba%E6%90%AD%E9%85%8D%E5%85%B3%E7%B3%BB"><span class="toc-number">1.5.</span> <span class="toc-text">springcloud、springboot、springalibaba搭配关系</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#nacos%E4%BD%9C%E4%B8%BA%E6%B3%A8%E5%86%8C%E3%80%81%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83"><span class="toc-number">1.6.</span> <span class="toc-text">nacos作为注册、配置中心</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#1-%E6%B3%A8%E5%86%8C%E6%9C%8D%E5%8A%A1-EnableDiscoveryClient"><span class="toc-number">1.6.0.1.</span> <span class="toc-text">1. 注册服务@EnableDiscoveryClient</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#2-%E6%83%B3%E8%A6%81%E8%BF%9C%E7%A8%8B%E8%B0%83%E7%94%A8%E5%88%AB%E7%9A%84%E6%9C%8D%E5%8A%A1"><span class="toc-number">1.6.0.2.</span> <span class="toc-text">2. 想要远程调用别的服务</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#3-%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83"><span class="toc-number">1.6.0.3.</span> <span class="toc-text">3. 配置中心</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#API%E7%BD%91%E5%85%B3%E3%80%81springcloud-gateway"><span class="toc-number">1.7.</span> <span class="toc-text">API网关、springcloud-gateway</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%89%8D%E7%AB%AF%E9%A1%B9%E7%9B%AE%E6%90%AD%E5%BB%BA"><span class="toc-number">2.</span> <span class="toc-text">前端项目搭建</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#ES6"><span class="toc-number">2.1.</span> <span class="toc-text">ES6</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Node-js"><span class="toc-number">2.2.</span> <span class="toc-text">Node.js</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Vue"><span class="toc-number">2.3.</span> <span class="toc-text">Vue</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-Vue%E6%8C%87%E4%BB%A4"><span class="toc-number">2.3.1.</span> <span class="toc-text">1.Vue指令</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E5%92%8C%E4%BE%A6%E5%90%AC%E5%99%A8"><span class="toc-number">2.3.2.</span> <span class="toc-text">2.计算属性和侦听器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E7%BB%84%E4%BB%B6%E5%8C%96"><span class="toc-number">2.3.3.</span> <span class="toc-text">3. 组件化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0"><span class="toc-number">2.3.4.</span> <span class="toc-text">4. 生命周期钩子函数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5-vue-%E6%A8%A1%E5%9D%97%E5%8C%96%E5%BC%80"><span class="toc-number">2.3.5.</span> <span class="toc-text">5. vue 模块化开</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%95%86%E5%93%81%E6%9C%8D%E5%8A%A1%E7%B3%BB%E7%BB%9F"><span class="toc-number">3.</span> <span class="toc-text">商品服务系统</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%95%86%E5%93%81%E5%88%86%E7%B1%BB"><span class="toc-number">3.1.</span> <span class="toc-text">商品分类</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1-%E6%9F%A5%E5%87%BA%E6%89%80%E6%9C%89%E5%88%86%E7%B1%BB%E5%8F%8A%E5%AD%90%E5%88%86%E7%B1%BB%EF%BC%8C%E7%94%A8%E6%A0%91%E5%BD%A2%E7%BB%93%E6%9E%84%E8%BF%94%E5%9B%9E%EF%BC%88%E5%95%86%E5%93%81%E4%B8%89%E7%BA%A7%E5%88%86%E7%B1%BB%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%EF%BC%89"><span class="toc-number">3.1.1.</span> <span class="toc-text">1. 查出所有分类及子分类，用树形结构返回（商品三级分类的增删改查）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2-%E8%B7%A8%E5%9F%9F"><span class="toc-number">3.1.2.</span> <span class="toc-text">2. 跨域</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3-%E5%89%8D%E7%AB%AF%E4%B8%89%E7%BA%A7%E5%88%86%E7%B1%BB%E7%BB%84%E4%BB%B6"><span class="toc-number">3.1.3.</span> <span class="toc-text">3. 前端三级分类组件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4-%E4%B8%89%E7%BA%A7%E5%88%86%E7%B1%BB%E7%BB%84%E4%BB%B6%E4%B8%AD%E9%83%A8%E5%88%86%E7%9A%84%E9%80%BB%E8%BE%91%E5%88%A0%E9%99%A4"><span class="toc-number">3.1.4.</span> <span class="toc-text">4. 三级分类组件中部分的逻辑删除</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%93%81%E7%89%8C%E7%AE%A1%E7%90%86"><span class="toc-number">3.2.</span> <span class="toc-text">品牌管理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#aliyun%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8OSS"><span class="toc-number">3.2.1.</span> <span class="toc-text">aliyun对象存储OSS</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%AD%BE%E5%90%8D%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6"><span class="toc-number">3.2.2.</span> <span class="toc-text">服务端签名上传文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%89%8D%E5%90%8E%E7%AB%AF%E8%A1%A8%E5%8D%95%E6%95%B0%E6%8D%AE%E9%83%BD%E8%BF%9B%E8%A1%8C%E6%A0%A1%E9%AA%8CJSR303"><span class="toc-number">3.2.3.</span> <span class="toc-text">前后端表单数据都进行校验JSR303</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E4%B8%AD%E8%BF%9B%E8%A1%8C%E6%A0%A1%E9%AA%8C%EF%BC%8CcontrollerAdvice"><span class="toc-number">3.2.4.</span> <span class="toc-text">统一异常处理中进行校验，controllerAdvice</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%86%E7%BB%84%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C"><span class="toc-number">3.2.5.</span> <span class="toc-text">分组数据校验</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A0%A1%E9%AA%8C"><span class="toc-number">3.2.6.</span> <span class="toc-text">自定义校验</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#mybatis-plus%E5%88%86%E9%A1%B5%E6%8F%92%E4%BB%B6"><span class="toc-number">3.2.7.</span> <span class="toc-text">mybatis-plus分页插件</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B9%B3%E5%8F%B0%E5%B1%9E%E6%80%A7"><span class="toc-number">3.3.</span> <span class="toc-text">平台属性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#spu%E3%80%81sku%E3%80%81%E8%A7%84%E6%A0%BC%E5%8F%82%E6%95%B0%E3%80%81%E9%94%80%E5%94%AE%E5%B1%9E%E6%80%A7"><span class="toc-number">3.3.1.</span> <span class="toc-text">spu、sku、规格参数、销售属性</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BA%A7%E8%81%94%E6%9B%B4%E6%96%B0"><span class="toc-number">3.3.2.</span> <span class="toc-text">级联更新</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Object%E5%88%92%E5%88%86%E3%80%81VO%E5%BA%94%E7%94%A8"><span class="toc-number">3.3.3.</span> <span class="toc-text">Object划分、VO应用</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%95%86%E5%93%81%E7%BB%B4%E6%8A%A4"><span class="toc-number">3.4.</span> <span class="toc-text">商品维护</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%BA%93%E5%AD%98%E7%B3%BB%E7%BB%9F"><span class="toc-number">4.</span> <span class="toc-text">库存系统</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#ElasticSearch%E5%85%A8%E6%96%87%E6%A3%80%E7%B4%A2%E5%8A%9F%E8%83%BD"><span class="toc-number">5.</span> <span class="toc-text">ElasticSearch全文检索功能</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AD%98%E5%82%A8"><span class="toc-number">5.1.</span> <span class="toc-text">存储</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%86%E6%9E%90"><span class="toc-number">5.2.</span> <span class="toc-text">分析</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A3%80%E7%B4%A2"><span class="toc-number">5.3.</span> <span class="toc-text">检索</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%86%E8%AF%8D%E5%99%A8"><span class="toc-number">5.4.</span> <span class="toc-text">分词器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#sku%E5%9C%A8es%E4%B8%AD%E7%9A%84%E5%AD%98%E5%82%A8%E6%A8%A1%E5%9E%8B"><span class="toc-number">5.5.</span> <span class="toc-text">sku在es中的存储模型</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BF%94%E5%9B%9E%E5%80%BCR%E7%9A%84%E8%AE%BE%E8%AE%A1%E7%BB%86%E8%8A%82"><span class="toc-number">5.6.</span> <span class="toc-text">返回值R的设计细节</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#feign%E7%9A%84%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B"><span class="toc-number">5.7.</span> <span class="toc-text">feign的调用流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%95%86%E5%9F%8E%E4%B8%9A%E5%8A%A1thymleaf%E5%BC%80%E5%8F%91"><span class="toc-number">5.8.</span> <span class="toc-text">商城业务thymleaf开发</span></a></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/2022/05/02/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%89%8D%E7%BC%80%E6%95%B0/" title="前缀树"><img src="https://s1.ax1x.com/2022/05/19/OHApGR.md.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="前缀树"/></a><div class="content"><a class="title" href="/2022/05/02/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%89%8D%E7%BC%80%E6%95%B0/" title="前缀树">前缀树</a><time datetime="2022-05-01T16:37:00.000Z" title="发表于 2022-05-02 00:37:00">2022-05-02</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/30/%E4%BD%BF%E7%94%A8%E5%BF%85%E8%AF%BB/" title="Ikiler在线 Markdown"><img src="https://s1.ax1x.com/2022/05/19/OHAkqO.md.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="Ikiler在线 Markdown"/></a><div class="content"><a class="title" href="/2022/04/30/%E4%BD%BF%E7%94%A8%E5%BF%85%E8%AF%BB/" title="Ikiler在线 Markdown">Ikiler在线 Markdown</a><time datetime="2022-04-29T16:37:00.000Z" title="发表于 2022-04-30 00:37:00">2022-04-30</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/29/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/" title="平衡二叉树"><img src="https://s1.ax1x.com/2022/05/19/OHA9R1.md.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="平衡二叉树"/></a><div class="content"><a class="title" href="/2022/04/29/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/" title="平衡二叉树">平衡二叉树</a><time datetime="2022-04-28T16:37:00.000Z" title="发表于 2022-04-29 00:37:00">2022-04-29</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/04/29/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%BA%BF%E6%80%A7%E8%A1%A8/" title="线性表"><img src="https://s1.ax1x.com/2022/05/19/OHApGR.md.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="线性表"/></a><div class="content"><a class="title" href="/2022/04/29/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E7%BA%BF%E6%80%A7%E8%A1%A8/" title="线性表">线性表</a><time datetime="2022-04-28T16:37:00.000Z" title="发表于 2022-04-29 00:37:00">2022-04-29</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/2022/01/29/%E4%BA%92%E8%81%94%E7%BD%91%E7%94%9F%E6%80%81/Docker/" title="docker"><img src="https://s1.ax1x.com/2022/05/19/OHACxx.md.jpg" onerror="this.onerror=null;this.src='/img/404.jpg'" alt="docker"/></a><div class="content"><a class="title" href="/2022/01/29/%E4%BA%92%E8%81%94%E7%BD%91%E7%94%9F%E6%80%81/Docker/" title="docker">docker</a><time datetime="2022-01-28T16:37:00.000Z" title="发表于 2022-01-29 00:37:00">2022-01-29</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By Sun Moon</div><div class="footer_custom_text"><a href="https://beian.miit.gov.cn/"  style="color:white" target="_blank">晋ICP备2022004590号-1</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js"></script><div class="js-pjax"></div><script src="https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0"></script><script async src="/js/weather.js"></script><script src="https://myhkw.cn/player/js/jquery.min.js" type="text/javascript"></script><script src="https://myhkw.cn/api/player/1651412271121" id="myhk" key="1651412271121" m="1"></script><script id="canvas_nest" defer="defer" color="0,0,255" opacity="0.7" zIndex="-1" count="99" mobile="true" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js"></script><script id="click-show-text" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-show-text.min.js" data-mobile="true" data-text="暴帅,暴富,健康,happy" data-fontsize="15px" data-random="true" async="async"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script><script src="https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js"></script><script src="https://cdn.jsdelivr.net/npm/pjax/pjax.min.js"></script><script>let pjaxSelectors = ["title","#config-diff","#body-wrap","#rightside-config-hide","#rightside-config-show",".js-pjax"]

var pjax = new Pjax({
  elements: 'a:not([target="_blank"])',
  selectors: pjaxSelectors,
  cacheBust: false,
  analytics: false,
  scrollRestoration: false
})

document.addEventListener('pjax:send', function () {

  // removeEventListener scroll 
  window.tocScrollFn && window.removeEventListener('scroll', window.tocScrollFn)
  window.scrollCollect && window.removeEventListener('scroll', scrollCollect)

  typeof preloader === 'object' && preloader.initLoading()
  document.getElementById('rightside').style.cssText = "opacity: ''; transform: ''"
  
  if (window.aplayers) {
    for (let i = 0; i < window.aplayers.length; i++) {
      if (!window.aplayers[i].options.fixed) {
        window.aplayers[i].destroy()
      }
    }
  }

  typeof typed === 'object' && typed.destroy()

  //reset readmode
  const $bodyClassList = document.body.classList
  $bodyClassList.contains('read-mode') && $bodyClassList.remove('read-mode')

})

document.addEventListener('pjax:complete', function () {
  window.refreshFn()

  document.querySelectorAll('script[data-pjax]').forEach(item => {
    const newScript = document.createElement('script')
    const content = item.text || item.textContent || item.innerHTML || ""
    Array.from(item.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value))
    newScript.appendChild(document.createTextNode(content))
    item.parentNode.replaceChild(newScript, item)
  })

  GLOBAL_CONFIG.islazyload && window.lazyLoadInstance.update()

  typeof chatBtnFn === 'function' && chatBtnFn()
  typeof panguInit === 'function' && panguInit()

  // google analytics
  typeof gtag === 'function' && gtag('config', '', {'page_path': window.location.pathname});

  // baidu analytics
  typeof _hmt === 'object' && _hmt.push(['_trackPageview',window.location.pathname]);

  typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting()

  // prismjs
  typeof Prism === 'object' && Prism.highlightAll()

  typeof preloader === 'object' && preloader.endLoading()
})

document.addEventListener('pjax:error', (e) => {
  if (e.request.status === 404) {
    pjax.loadUrl('/404.html')
  }
})</script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>